// HTTPS请求-带缓存、选项的V2版
package https

import (
	"context"
	"net/http/cookiejar"
	"time"
)

// 网址服务对象
type CURL struct {
	URI            string            `json:"uri"`         // 请求网址
	ParamQuest     map[string]string `json:"param"`       // 请求参数
	HttpCode       int               `json:"http_code"`   // HTTP返回的code值
	ParamJsonQuest map[string]any    `json:"param_json"`  // JSON推送参数
	Body           string            `json:"body"`        // 返回值
	Error          error             `json:"error"`       // 错误信息
	HeaderQuest    map[string]string `json:"header"`      // 请求Header头
	CreateTime     time.Time         `json:"create_time"` // 实例创建时间
	StartTime      time.Time         `json:"start_time"`  // 请求开始时间
	EndTime        time.Time         `json:"end_time"`    // 请求结束时间
	OverTime       time.Time         `json:"over_time"`   // JSON提取结束时间
	ClientIP       string            `json:"client_ip"`   // 请求的客户端IP【兼容日志处理模块的预留字段】
	CookieQuest    []string          `json:"cookie"`      // HTTP网址返回的Set-Cookie相应头
	Version        float64           `json:"version"`     // HTTP返回的Header头中的版本信息
	option         *Option           `json:"-"`           // 请求的选项信息
	files          map[string]string `json:"-"`           // 文件列表
}

// 外部选项传参结构[可选参数]
type Option struct {
	// 以下为可选配置项
	httpsContinue bool              // 是否跳过HTTPS证书效验
	timeOut       time.Duration     // 请求超时时间
	cacheTime     time.Duration     // 缓存时间【0表示不设置缓存】
	ignoreHeader  []string          // 忽略请求头
	noLog         bool              // 是否取消日志记录
	jar           *cookiejar.Jar    // jar信息
	context       context.Context   // 中断信息
	hosts         map[string]string // Host程序指定域名解析脚本

	// 以下为程序内部配置项
	cacheHit       bool     // 是否命中缓存
	retry          int      // 重试次数，默认为0表示不启用重试机制
	retryUri       string   // 错误报告地址
	reportList     []string // 错误报告内容
	successContent string   // 成功返回内容【返回字符串中包含此内容即视为成功】
	method         string   // 请求方法
	disableHttpV2  bool     // 是否禁用HTTP/2协议
}

// 选项设置参数，用于设置本次请求中的选项信息
type OptionFunc func(c *Option)

// 内部配置结构体
type config struct {
	LogFunc        []func(c *CURL)          // 日志回写函数【缓存命中后将不记录此处的请求日志】
	timeOut        time.Duration            // 默认超时时间
	cacheTime      time.Duration            // 默认缓存时间
	cacheInterface CacheInterface           // 缓存配置项
	jar            *cookiejar.Jar           // cookie值的jar
	saveCookie     bool                     // 是否存储cookie值
	UA             string                   // 默认的UA请求头
	CT             string                   // 默认的Content-Type请求头
	retryList      []time.Duration          // 重试间隔列表【重试机制开启时生效】
	hookBefore     map[string]func(c *CURL) // 请求前的钩子函数
	hookAfter      map[string]func(c *CURL) // 请求后的钩子函数
}

// 缓存钩子
type CacheInterface interface {
	Get(name string) string                       // 获取缓存值，缓存未找到的话请返回空字符串
	Set(name string, val string, t time.Duration) // 设置缓存值，可能存在部分无法在string中正常显示的字符串
}

// 程序调用的默认值信息
var _default config = config{
	timeOut:        time.Second * 60,
	cacheTime:      0,
	cacheInterface: &cache{},
	UA:             "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 HBPC/12.1.4.300",
	CT:             "application/json",
	retryList: []time.Duration{
		time.Second * 1,
		time.Second * 2,
		time.Second * 5,
		time.Second * 10,
		time.Second * 20,
		time.Second * 30,
		time.Minute * 1,
		time.Minute * 3,
		time.Minute * 5,
		time.Minute * 10,
		time.Minute * 30,
	},
	hookBefore: map[string]func(c *CURL){},
	hookAfter:  map[string]func(c *CURL){},
}

// 设置请求前的钩子函数
// 此处为使用url.Parse(XXXX).Host的纯域名，请勿添加http之类的前缀
// 另，所有域名通用的钩子函数请使用"*"作为域名
// 最后，后添加的钩子函数会覆盖前面的同名钩子函数，经添加钩子时先查找下项目内是否已存在该钩子
//
//	domain	域名
//	funct	请求前的钩子函数
func SetBeforeHook(domain string, funct func(c *CURL)) {
	if domain == "" {
		domain = "*"
	}
	if funct == nil {
		return
	}
	_default.hookBefore[domain] = funct
}

// 设置请求后的钩子函数
// 此处为使用url.Parse(XXXX).Host的纯域名，请勿添加http之类的前缀
// 另，所有域名通用的钩子函数请使用"*"作为域名
// 最后，后添加的钩子函数会覆盖前面的同名钩子函数，经添加钩子时先查找下项目内是否已存在该钩子
//
//	domain	域名
//	funct	请求后的钩子函数
func SetAfterHook(domain string, funct func(c *CURL)) {
	if domain == "" {
		domain = "*"
	}
	if funct == nil {
		return
	}
	_default.hookAfter[domain] = funct
}

// 设置Quest请求日志
//
//	f	日志记录函数【此函数会记录多个，所以请勿一直调用SetQuestLog进行插入日志记录函数】
func SetQuestLog(f func(c *CURL)) {
	if f == nil {
		return
	}
	_default.LogFunc = append(_default.LogFunc, f)
}

// 清除Quest请求日志记录的函数列表
func ClearQuestLog() {
	_default.LogFunc = nil
}

// 设置默认的超时时间
//
//	t	超时时间
func SetDefaultTimeOut(t time.Duration) {
	_default.timeOut = t
}

// 设置默认的user-agent请求头
//
//	ua	UA请求头
func SetDefaultUa(ua string) {
	_default.UA = ua
}

// 设置默认的Content-Type请求头
//
//	ct	Content-Type请求头
func SetDefaultCt(ct string) {
	_default.CT = ct
}

// 设置默认的缓存时间
// 不建议设置此值，若确实需要缓存的话，建议针对URL地址进行配置缓存时间
//
//	t	缓存时间
func SetDefaultCacheTime(t time.Duration) {
	_default.cacheTime = t
}

// 设置缓存信息
//
//	t	实现缓存方法的接口信息
func SetDefaultCacheFunc(t CacheInterface) {
	if t == nil {
		return
	}
	_default.cacheInterface = t
}

// 设置cookie存储的jar信息
//
//	jar	存储的jar信息，默认为nil，nil会获取新的cookiejar.New的jar结构体
func SetJar(jar *cookiejar.Jar) {
	_default.jar = jar
}

// 设置默认是否存储cookie
//
//	s	是否存储cookie值，默认存储，可以使用此方法跳过cookie的存储
func SetDefaultUnCookieJar(s bool) {
	_default.saveCookie = s
}
